#include "bsp_clk.h"      //配置用头文件在 imx6ul.h文件中

/**
 *  @brief  :使能I.MX6U所有外设时钟
 *  @param  :无
 *  @retval :无
 */
void clk_enable(void)
{
    CCM->CCGR0= 0xFFFFFFFF;
    CCM->CCGR1= 0xFFFFFFFF;
    CCM->CCGR2= 0xFFFFFFFF;
    CCM->CCGR3= 0xFFFFFFFF;
    CCM->CCGR4= 0xFFFFFFFF;
    CCM->CCGR5= 0xFFFFFFFF;
    CCM->CCGR6= 0xFFFFFFFF;
}


/**
 *  @brief  :初始化系统时钟为528MHz,并且设置PLL2和PLL3各个PFD时钟,所有的时钟频率都设置为官方推荐值
 *  @param  :无
 *  @retval :无
 */
void imx6ul_clkinit(void)
{
    uint32_t reg=0;

    /**
     *  1.设置ARM内核时钟为528MHz
     * 
     *  1.1 判断使用哪个时钟源启动的,正常情况下是由PLL1_SW_CLK驱动的,而PLL1_SW_CLK有两个来源:PLL1_MAIN_CLK和STEP_CLK.如果要让IMX6ULL跑到528MHz,那就必须
     *      选择PLL1_MAIN_CLK作为PLL1的时钟源.如果要修改PLL1_MAIN_CLK时钟的话那就必须先将PLL1_SW_CLK从PLL1_MAIN_CLK切换到STEP_CLK,修改完以后再将
     *      PLL1_SW_CLK 切换回PLL1_MAIN_CLK .STEP_CLK等于24MHz
     */
    if( ( ((CCM->CCSR) >> 2) & 0x1 ) ==0)
    {   //当前PLL1_SW_CLK的时钟源为PLL1_MAIN_CLK
        CCM->CCSR  &= ~(1<<8);      //配置STEP_CLK时钟源为24MHz OSC   配置之前必须未使用
        CCM->CCSR |= (1 << 2);      //配置PLL1_SW_CLK时钟源为STEP_CLK
    }

    /**
     * 1.2 设置PLL1_MAIN_CLK为1056MHz,也就是528*2=1056MHz
     *     因为PLL1_SW_CLK进入ARM内核的时候会被二分频!
     *     配置CCM_ANLOG->PLL_ARM寄存器
     *     bit13 : 1 使能时钟输出
     *     bit[6:0] :88 ,由公式:Fout = Fin *div_select /2.0 ,
     *     1056 =24 * div_select/2.0 ,得出: div_select=88    上面已经将时钟设置为了STEP_CLK
     */
    CCM_ANALOG->PLL_ARM = ( 1<< 13 ) | ((88<<0) & 0x7F);
    CCM->CCSR &=  ~(1<<2);   //将PLL1_SW_CLK时钟切换回PLL1_MAIN_CLK
    CCM->CACRR=1;   //ARM内核时钟为PLL1_SW_CLK/2 =1056/2 =528MHz    这里是将2112MHz的PLL1_SW_CLK进行两分频  

    /**
     * 2.设置PLL2(SYS_PLL)各个PFD
    */
    reg = CCM_ANALOG->PFD_528;   //将原来的值复制过来
    reg &= ~(0X3F3F3F3F);        //清除原来的设置
    reg |= 32<<24;               //PLL2_PFD3=528*18/32 = 297MHz
    reg |= 24<<16;               //PLL2_PFD2=528*18/24 = 396MHz
    reg |= 16<<8;                //PLL2_PFD1=528*18/16 = 594MHz
    reg |= 27<<0;                //PLL2_PFD0=528*18/27 = 352MHz  
    CCM_ANALOG->PFD_528=reg;     //设置PLL2_PFD0~3

    /** 
     *  3.设置PLL3(USB1)各个PFD 
     */
    reg=0;    
    reg=CCM_ANALOG->PFD_480;
    reg &= ~(0X3F3F3F3F);        //清除原来的设置
    reg |= 19<24;                //PLL3_PFD3=480*18/19 = 454.74MHz
    reg |= 17<<16;               //PLL3_PFD2=480*18/17 = 508.24MHz
    reg |= 16<<8;                //PLL3_PFD1=480*18/16 = 540MHz
    reg |= 12<<0;                //PLL3_PFD0=480*18/12 = 720MHz  
    CCM_ANALOG->PFD_480=reg;     //设置PLL3_PFD0~3

    /**
     * 4.设置AHB时钟 最小6MHz,最大132MHz 
     */
    CCM->CBCMR &= ~(3<<18);   //清除设置
    CCM->CBCMR |= (1<<18);    //PRE_PERIPH_CLK = PLL2_PFD2 = 396MHz   peripheral
    CCM->CBCDR &= ~(1<<25);   //PERIPH_CLK=PRE_PERIPH_CLK=396MHz
    while (CCM->CDHIPR & (1<<5))
    {
        ;  //等待握手完成
    }
    /**
     * 修改AHB_PODF位的时候需要先禁止AHB_CLK_ROOT的输出,但是目前还没有找到关闭AHB_CLK_ROOT输出的寄存器,所以就没办法设置
     * 下面的设置AHB_PODF的代码仅供学习参考,不能拿来直接使用!!!!!!!!!
     * 内部的boot rom 将AHB_PODF设置为了3分频, 即使我们不设置AHB_PODF  AHB_ROOT_CLK也依旧等于396/3= 132MHz
     */
#if  0
    //要先关闭AHB_ROOT_CLK输出,否则时钟设置会出错
    CCM->CBCDR &= ~(7<<10);    //CBCDR的AHB_PODF清零
    CCM->CBCDR |= 2<<10;       //AHB_PODF3分频,AHB_CLK_ROOT=132MHz
    while(CCM->CDHIPR & (1<<1)) //等待握手完成
    {
        ;  
    }
#endif
    /**
     * 5.设置IPG_CLK_ROOT最小3MHz,最大66MHz 
     */
    CCM->CBCDR &= ~(3<<8);    //CBCDR的IPG_PODF清零
    CCM->CBCDR |= 1<<8;       //IPG_PODF二分频  IPG_CLK_ROOT等于66MHz

    /**
     * 6.设置PERCLK_CLK_ROOT时钟 
     */
    CCM->CSCMR1 &= ~(1<<6);  //PERCLK_CLK_ROOT时钟源为IPG
    CCM->CSCMR1 &= ~(7<<0);  //PERCLK_PODF位清零,即1分频
}